/*
 *      Author: Honrun
 */
#include "stdint.h"
#include "stdio.h"
#include "gd32f30x.h"
#include "DevicesTime.h"
#include "DevicesKey.h"


KeyType g_typeKey;


void vKeyInit(void)
{
    /* enable the GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_GPIOD);

    gpio_pin_remap_config(GPIO_PD01_REMAP, ENABLE);

    gpio_bit_set(KEY_UP_GPIO_Port,      KEY_UP_Pin);
    gpio_bit_set(KEY_DOWN_GPIO_Port,    KEY_DOWN_Pin);
    gpio_bit_set(KEY_LEFT_GPIO_Port,    KEY_LEFT_Pin);
    gpio_bit_set(KEY_RIGHT_GPIO_Port,   KEY_RIGHT_Pin);
    gpio_bit_set(KEY_SELECT_GPIO_Port,  KEY_SELECT_Pin);
    gpio_bit_set(KEY_START_GPIO_Port,   KEY_START_Pin);
    gpio_bit_set(KEY_A_GPIO_Port,       KEY_A_Pin);
    gpio_bit_set(KEY_B_GPIO_Port,       KEY_B_Pin);
    gpio_bit_set(KEY_X_GPIO_Port,       KEY_X_Pin);
    gpio_bit_set(KEY_Y_GPIO_Port,       KEY_Y_Pin);
    gpio_bit_set(KEY_HOME_GPIO_Port,    KEY_HOME_Pin);

    /* configure key pin as input */
    gpio_init(KEY_UP_GPIO_Port,         GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, KEY_UP_Pin);
    gpio_init(KEY_DOWN_GPIO_Port,       GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, KEY_DOWN_Pin);
    gpio_init(KEY_LEFT_GPIO_Port,       GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, KEY_LEFT_Pin);
    gpio_init(KEY_RIGHT_GPIO_Port,      GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, KEY_RIGHT_Pin);
    gpio_init(KEY_SELECT_GPIO_Port,     GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, KEY_SELECT_Pin);
    gpio_init(KEY_START_GPIO_Port,      GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, KEY_START_Pin);
    gpio_init(KEY_X_GPIO_Port,          GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, KEY_X_Pin);
    gpio_init(KEY_Y_GPIO_Port,          GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, KEY_Y_Pin);
    gpio_init(KEY_A_GPIO_Port,          GPIO_MODE_IPU, GPIO_OSPEED_2MHZ, KEY_A_Pin);
    gpio_init(KEY_B_GPIO_Port,          GPIO_MODE_IPD, GPIO_OSPEED_2MHZ, KEY_B_Pin);
    gpio_init(KEY_HOME_GPIO_Port,       GPIO_MODE_IPD, GPIO_OSPEED_2MHZ, KEY_HOME_Pin);

    g_typeKey.uiKeyValueGet = uiKeyValueGet;
}

/*
Return Value:   按键组合序号（头文件里有定义）
Parameters:     void
Description:    读取当前按键状态
*/
uint32_t uiKeyValueGet(void)
{
    uint32_t Key = KEY_ALL;
    uint8_t i = 0;

    for(i = 0; i < 8; ++i)
    {
        if(RESET != (GPIO_ISTAT(KEY_UP_GPIO_Port)       & KEY_UP_Pin))
            Key &= ~KEY_UP;
        if(RESET != (GPIO_ISTAT(KEY_DOWN_GPIO_Port)     & KEY_DOWN_Pin))
            Key &= ~KEY_DOWN;
        if(RESET != (GPIO_ISTAT(KEY_LEFT_GPIO_Port)     & KEY_LEFT_Pin))
            Key &= ~KEY_LEFT;
        if(RESET != (GPIO_ISTAT(KEY_RIGHT_GPIO_Port)    & KEY_RIGHT_Pin))
            Key &= ~KEY_RIGHT;
        if(RESET != (GPIO_ISTAT(KEY_SELECT_GPIO_Port)   & KEY_SELECT_Pin))
            Key &= ~KEY_SELECT;
        if(RESET != (GPIO_ISTAT(KEY_START_GPIO_Port)    & KEY_START_Pin))
            Key &= ~KEY_START;
        if(RESET != (GPIO_ISTAT(KEY_X_GPIO_Port)        & KEY_X_Pin))
            Key &= ~KEY_X;
        if(RESET != (GPIO_ISTAT(KEY_Y_GPIO_Port)        & KEY_Y_Pin))
            Key &= ~KEY_Y;
        if(RESET != (GPIO_ISTAT(KEY_A_GPIO_Port)        & KEY_A_Pin))
            Key &= ~KEY_A;
        if(RESET == (GPIO_ISTAT(KEY_B_GPIO_Port)        & KEY_B_Pin))
            Key &= ~KEY_B;
        if(RESET == (GPIO_ISTAT(KEY_HOME_GPIO_Port)     & KEY_HOME_Pin))
            Key &= ~KEY_HOME;
    }

    return Key;
}

/*
 * Return:      按键状态
 * Parameters:  Value_new:新按键键值; Value_old:旧按键键值;
 * Description: 按键状态获取
 */
static KeyStateEnum enumKeyStateGet(uint32_t uiNew, uint32_t uiOld)
{
    if(uiNew == uiOld)                      /* 当前按键值与上次按键值相同 */
        return keyEqual;
    else if((uiNew & uiOld) == uiOld)       /* 当前按键值在上次按键值基础上增加了新按键 */
        return keyAdd;
    else if((uiNew | uiOld) == uiOld)       /* 当前按键值在上次按键值基础上减少了旧按键 */
        return keyDel;
    else
        return keyAddAndDel;
}

/*
 * Return:      KeyStateEnum：按键状态机的状态
 * Parameters:  ptypeKeyMachine: 按键结构消息
 * Description: 按键状态获取
 */
KeyStateEnum enumKeyMachine(KeyType *ptypeKey)
{
    uint32_t uiKeyNew;
    uint16_t usTimeNow = lTimebaseGet() / 1000, usTimeDelta;

    if((ptypeKey == NULL) || (ptypeKey->uiKeyValueGet == NULL))
        return keyNormal;

    if((uiKeyNew = ptypeKey->uiKeyValueGet()) & 0x8000)
        return keyNormal;

    /* 记录时间、键值变化 */
    switch(enumKeyStateGet(uiKeyNew, ptypeKey->valueLast))
    {
        /* 记录按键按下时间、变化总键值 */
        case keyAdd:
        case keyAddAndDel:
            ptypeKey->valueDown = uiKeyNew;
            ptypeKey->timeDown  = usTimeNow;
            ptypeKey->state     = keyAdd;
            ptypeKey->valueLast = uiKeyNew;
            ptypeKey->cntCombo += (ptypeKey->valueDown == ptypeKey->valueUp);
            break;

        /* 记录按键松开时间、变化总键值 */
        case keyDel:
            ptypeKey->valueUp  |= ptypeKey->valueLast & (~uiKeyNew);
            ptypeKey->timeUp    = usTimeNow;
            ptypeKey->state    |= keyDel;
            ptypeKey->valueLast = uiKeyNew;
            break;

        default: break;
    }

    /* 按键状态转换 */
    switch((uint32_t)ptypeKey->state)
    {
        case keyAdd:
            usTimeDelta = (KEY_SHORT_TIME * ptypeKey->cntCombo) > 500 ? 500 : (KEY_SHORT_TIME * ptypeKey->cntCombo);
            if((usTimeNow - ptypeKey->timeDown) >= usTimeDelta)
            {
                ptypeKey->state = keyShort;
                return ptypeKey->state;
            }
            break;

        case keyShort:
            if((usTimeNow - ptypeKey->timeDown) >= KEY_LONG_TIME)
            {
                ptypeKey->state = keyLong;
                return ptypeKey->state;
            }
            break;

        case keyLong:
            if((usTimeNow - ptypeKey->timeDown) >= KEY_CONTINUE_TIME)
            {
                ptypeKey->state = keyContinuous;
            }
            break;

        case keyContinuous:
            if((usTimeNow - ptypeKey->timeDown) >= KEY_CONTINUE_INTERVAL_TIME)
            {
                ptypeKey->timeDown = usTimeNow;
                return ptypeKey->state;
            }
            break;

        case keyDel | keyAdd:
        case keyDel | keyShort:
        case keyDel | keyLong:
        case keyDel | keyContinuous:
            usTimeDelta = (KEY_SHORT_TIME * ptypeKey->cntCombo) > 500 ? 500 : (KEY_SHORT_TIME * ptypeKey->cntCombo);
            if(((usTimeNow - ptypeKey->timeUp) >= usTimeDelta) && (uiKeyNew == 0))
            {
                ptypeKey->state  = (ptypeKey->state & 0xFF00) | keyUp;
                return ptypeKey->state;
            }
            break;

        default : ptypeKey->cntCombo = 1; ptypeKey->valueUp = 0; break;
    }

    return keyNormal;
}
